package com.atguigu.tingshu.account.service.impl;

import com.atguigu.tingshu.account.mapper.UserAccountMapper;
import com.atguigu.tingshu.account.service.UserAccountService;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.account.UserAccount;
import com.atguigu.tingshu.model.account.UserAccountDetail;
import com.atguigu.tingshu.vo.account.AccountLockResultVo;
import com.atguigu.tingshu.vo.account.AccountLockVo;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class UserAccountServiceImpl extends ServiceImpl<UserAccountMapper, UserAccount> implements UserAccountService {

    @Autowired
    private UserAccountMapper userAccountMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    @Transactional
    public void initUserAccount(Long userId) {
        UserAccount userAccount = new UserAccount();
        userAccount.setUserId(userId);
        userAccountMapper.insert(userAccount);
    }

    @Override
    public BigDecimal getAvailableAmount() {
        Long userId = AuthContextHolder.getUserId();
        UserAccount one = this.lambdaQuery().eq(UserAccount::getUserId, userId).one();
        return one.getAvailableAmount();
    }

    @Transactional
    @Override
    public AccountLockResultVo checkAndLockAccount(AccountLockVo accountLockVo) {
        String orderNo = accountLockVo.getOrderNo();
        String checkKey = RedisConstant.ACCOUNT_CHECK_LOCK_PREFIX + orderNo;
        String dataKey = RedisConstant.ACCOUNT_CHECK_INFO_PREFIX + orderNo;
        // 判断同一个订单是否重复验余额并锁定余额
        Boolean flag = redisTemplate.opsForValue().setIfAbsent(checkKey, orderNo, 1, TimeUnit.HOURS);
        AccountLockResultVo accountLockResultVo = null;
        if (!flag) {
            // 重复锁定的情况下，从缓存中获取订单的锁定结果（缓存锁定结果的目的是为了方便之后扣减余额）
            accountLockResultVo = (AccountLockResultVo) redisTemplate.opsForValue().get(dataKey);
            // 若缓存结果为空，代表锁余额的步骤还未结束
            if (accountLockResultVo == null) {
                throw new GuiguException(ResultCodeEnum.ACCOUNT_LOCK_REPEAT);
            }
            return accountLockResultVo;
        }
        // 查询用户余额，并锁定余额
        int result = userAccountMapper.checkAndLockAmount(accountLockVo.getUserId(), accountLockVo.getAmount());
        if (result == 0) {
            // 锁定余额失败，释放锁
            redisTemplate.delete(checkKey);
            throw new GuiguException(ResultCodeEnum.ACCOUNT_LOCK_ERROR);
        }
        // 记录账户明细
        UserAccountDetail userAccountDetail = UserAccountDetail.builder()
                .title(accountLockVo.getContent())
                .tradeType(SystemConstant.ACCOUNT_TRADE_TYPE_LOCK)
                .build();
        BeanUtils.copyProperties(accountLockVo, userAccountDetail);
        Db.save(userAccountDetail);
        // 缓存结果到redis
        accountLockResultVo = new AccountLockResultVo();
        BeanUtils.copyProperties(accountLockVo, accountLockResultVo);
        redisTemplate.opsForValue().set(dataKey, accountLockResultVo, RedisConstant.USER_TRADE_TIMEOUT, TimeUnit.MINUTES);
        return accountLockResultVo;
    }
}
